/**
 * @class   DiscoveryUdpImpl
 * @brief   BundleGenericWorker of wosh::services::DiscoveryUdpBundle
 *
 * All public methods are reentrant.
 *
 * Implementation is based on WOSH wosh::network::SocketUdp.
 * Discovery packet is represented by DiscoveryUdpMessage, which implements
 * serialization.
 *
 ****************************************************************************
 * @version 0.8.384 $Id: DiscoveryUdpImpl.h 2323 2010-04-22 13:56:45Z alex $
 * @date    Released Stable on 2010-02-21
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Bundles_Services_DiscoveryUdpImpl_H__
 #define __WOSH_Bundles_Services_DiscoveryUdpImpl_H__

 #include <core/BundleGenericWorker.h>
 #include <core/WoshHost.h>
 #include <framework/network/SocketUdp.h>
 #include "DiscoveryUdpMessage.h"
 

class DiscoveryUdpImpl :	public wosh::BundleGenericWorker,
							public wosh::network::ISocketUdpListener
 {
	WOSH_CLASSNAME(DiscoveryUdpImpl)
	WOSH_CLASS_ABSTRACT(false)

	public:
		/**
		 * @brief   Empty constructor, init and set default vars.
		 * @param bundle [in] the parent bundle, see BundleGenericWorker interface
		 */
		DiscoveryUdpImpl( wosh::BundleGeneric& bundle );
		/**
		 * @brief   Empty destructor, may (synch) stop the internal threads when still running
		 */
		virtual ~DiscoveryUdpImpl();


	public:
		/**
		 * @brief   force sending of discovery message to target addresses. Reentrant.
		 * @param synch [in] when true the call is reentrant (blocking), else
		 *          it will reset the internal timer forcing transmission on next cycle (about one second)
		 * @return  WRET_ERR_ILLEGAL_USE when the thread is not running; WRET_ERR_INTERNAL on timeout error
		 *          (only if synch=true); WRET_OK else 
		 */
		virtual WRESULT sendDiscoveryUdpMessage( bool synch = true );


/** @name Setters (reentrant)
 * @{
 ******************************************************************************/
 	public:
		/**
		 * @brief   Set the interval between each trasmission of discovery message. Reentrant.
		 * @param seconds [in] notification frequency in seconds
		 */
		void setNotifyFrequency( unsigned int seconds );

		/**
		 * @brief   Set address/port the UDP server should listen to. Reentrant.
		 *          You must stop (and restart) the internal thread to change binding address.
		 * @param address_port [in] local binding address:port (such as '0.0.0.0:8787')
		 * @return  WRET_ERR_ILLEGAL_USE when the thread is running (socket is already binded); WRET_OK else 
		 */
		WRESULT setBindAddressPort( const std::string& address_port );

		/**
		 * @brief   Set list of destination hosts/port of the discovery messages. Reentrant.
		 * @param addresses [in] list of address:port (such as '192.168.0.1:8787', ..)
		 * @return  negative value (error count) when one or more addresses are invalid;
		 *          positive value on success (address count); 0 when empty
		 */
		int setBroadcastAddressPort( const std::vector<std::string>& addresses );

		/**
		 * @brief   Set list of destination hosts/port of the discovery messages. Reentrant.
		 *          Provided for convenience.
		 * @param addresses [in] list of address:port, separator is ';' (such as '192.168.0.1:8787;192.168.1.255:8777')
		 * @see    setBroadcastAddressPort()
		 */
		int setBroadcastAddressPort( const std::string& addresses );

//@}
/** @name Getters (reentrant)
 * @{
 ******************************************************************************/
 	public:
		/**
		 * @brief   Get the interval between each trasmission of discovery message. Reentrant.
		 * @return  notification frequency in seconds
		 */
		unsigned int getNotifyFrequency() const;

		/**
		 * @brief   Get address/port the UDP server is listening to. Reentrant.
		 * @return  local binding address:port (such as '0.0.0.0:8787')
		 */
		std::string getBindAddressPort() const;

		/**
		 * @brief   Get list of destination hosts/port of the discovery messages. Reentrant.
		 * @return  list of address:port (such as '192.168.0.1:8787', ..)
		 */
		std::vector<std::string> getBroadcastAddressPort() const;

//@}
/** @name SocketUdp Events
 * @{
 ******************************************************************************/
 	protected:
		/**
		 * @brief   Parse incoming datagrams (discovery messages). Forward information
		 *          to the NetworkManager.
		 * @param data [in] the serialized discovery message
		 * @param maxSize [in] size of the data
		 * @param source [in] remote UDP socket address
		 * @see     updateHostByMessage()
		 * @see     NetworkManager::updateWoshHost
		 */
		virtual void udpIncomingDatagram( const char * data, int maxSize, const std::string& source, wosh::network::SocketUdp* );

		friend class wosh::network::SocketUdp;
//@}
/** @name Internal methods
 * @{
 ******************************************************************************/
	protected:
		/**
		 * @brief   Updates the discovery message (against NetworkManager::local-host) and sends
		 *          the datagram to all target addresses (iterative, not reentrant).
		 * @see     updateHostByMessage
		 * @see     NetworkManager::getLocalWoshHost()
		 * @see     sendNow
		 */
		void sendMessage();

		/**
		 * @brief   Update the discovery message against WoshHost.
		 *          Fields are: Kernel-Name,Kernel-State, NotifyFrequency, SupportedProtocols, Neighbours
		 *          Used to construct the outgoing discovery message (containing updated local-host fields)
		 * @param message [out] the discovery message being updated
		 * @param host [in] the source WoshHost 
		 * @see     sendMessage()
		 */
		void updateMessageByHost( wosh::DiscoveryUdpMessage& message, const wosh::WoshHost& host );

		/**
		 * @brief   Update the WoshHost against discovery message.
		 *          Fields are: Seen, Kernel-Name, Kernel-State, DiscoverySource, DiscoveryBindAddress,
		 *          DiscoveryProtocolVersion, NotifyFrequency, SupportedProtocols, Neighbours
		 *          Used to parse the incoming discovery message and update the information of a remote WoshHost
		 * @param host [out] the WoshHost to be updated
		 * @param message [in] the source discovery message
		 * @see     udpIncomingDatagram()
		 */
		void updateHostByMessage( wosh::WoshHost& host, const wosh::DiscoveryUdpMessage& message );

		/**
		 * @brief   Bind the UDP socket. Forward some local fields to NetworkManager for
		 *          updating local-host (such as discovery-bind-address, discovery-notify-frequency)
		 * @see     setBindAddressPort()
		 * @see     NetworkManager::updateWoshHost()
		 * @see     false on binding errors; true else
		 */
		virtual bool initializingThread();
		/**
		 * @brief   Periodically send message(s), loop tick is about one second (200ms accurancy)
		 * @see     sendMessage()
		 * @see     sendNow
		 */
		virtual void runThread();
		/**
		 * @brief   UnBind the UDP socket.
		 */
		virtual void exitingThread();

//@}
/** @name Internal vars : core
 * @{
 ******************************************************************************/
	protected:
		wosh::network::SocketUdp* socket;	///< I/O udp socket
		bool sendNow;						///< flag used for asynch send request

		wosh::DiscoveryUdpMessage msgBase;	///< template of the discovery message (some fields are static)
		wosh::WoshHost currHost;			///< temporary host (last retrieved)

		char* msgBuffer;					///< static internal buffer for outgoing messages

//@}
/** @name Internal vars : settings
 * @{
 ******************************************************************************/
	protected:
		wosh::MutexRW settingsMux;			///< mutex for configuration changes
		unsigned int freqNotify;			///< period of scheduled sending in seconds 
		std::string bindAddressPort;		///< local address::port (such as 192.168.0.1::1234)
		std::vector<std::string> broadcastAddressPort;	///< target address::port list

//@}


}; // class def

#endif //__WOSH_Bundles_Services_DiscoveryUdpImpl_H__
